<template>
	<div
		class="el-progress"
		:class="[
			'el-progress--' + type,
			status ? 'is-' + status : '',
			{
				'el-progress--without-text': !showText,
				'el-progress--text-inside': textInside,
			}
		]"
		role="progressbar"
		:aria-valuenow="percentage"
		aria-valuemin="0"
		aria-valuemax="100"
	>
		<div class="el-progress-bar" v-if="type === 'line'">
			<div class="el-progress-bar__outer" :style="{height: strokeWidth + 'px'}">
				<div class="el-progress-bar__inner" :style="barStyle">
					<div class="el-progress-bar__innerText" v-if="showText && textInside">{{content}}</div>
				</div>
			</div>
		</div>
		<div class="el-progress-circle" :style="{height: width + 'px', width: width + 'px'}" v-else>
			<svg viewBox="0 0 100 100">
				<path
					class="el-progress-circle__track"
					:d="trackPath"
					stroke="#e5e9f2"
					:stroke-width="relativeStrokeWidth"
					fill="none"
					:style="trailPathStyle"></path>
				<path
					class="el-progress-circle__path"
					:d="trackPath"
					:stroke="stroke"
					fill="none"
					:stroke-linecap="strokeLinecap"
					:stroke-width="percentage ? relativeStrokeWidth : 0"
					:style="circlePathStyle"></path>
			</svg>
		</div>
		<div
			class="el-progress__text"
			v-if="showText && !textInside"
			:style="{fontSize: progressTextSize + 'px'}"
		>
			<template v-if="!status">{{content}}</template>
			<i v-else :class="iconClass"></i>
		</div>
	</div>
</template>
<script>
	export default {
		props: {
			type: {
				type: String,
				default: 'line',
				validator: val => ['line', 'circle', 'dashboard'].indexOf(val) > -1
			},
			percentage: {
				type: Number,
				default: 0,
				required: true,
				validator: val => val >= 0 && val <= 100
			},
			status: {
				type: String,
				validator: val => ['success', 'exception', 'warning'].indexOf(val) > -1
			},
			strokeWidth: {
				type: Number,
				default: 6
			},
			strokeLinecap: {
				type: String,
				default: 'round'
			},
			textInside: {
				type: Boolean,
				default: false
			},
			width: {
				type: Number,
				default: 126
			},
			showText: {
				type: Boolean,
				default: true
			},
			color: {
				type: [String, Array, Function],
				default: ''
			},
			format: Function
		},
		computed: {
			barStyle() {
				const style = {};
				style.width = this.percentage + '%';
				style.backgroundColor = this.getCurrentColor(this.percentage);
				return style;
			},
			relativeStrokeWidth() {
				return (this.strokeWidth / this.width * 100).toFixed(1);
			},
			radius() {
				if (this.type === 'circle' || this.type === 'dashboard') {
					return parseInt(50 - parseFloat(this.relativeStrokeWidth) / 2, 10);
				} else {
					return 0;
				}
			},
			trackPath() {
				const radius = this.radius;
				const isDashboard = this.type === 'dashboard';
				return `
					M 50 50
					m 0 ${isDashboard ? '' : '-'}${radius}
					a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '-' : ''}${radius * 2}
					a ${radius} ${radius} 0 1 1 0 ${isDashboard ? '' : '-'}${radius * 2}
					`;
			},
			perimeter() {
				return 2 * Math.PI * this.radius;
			},
			rate() {
				return this.type === 'dashboard' ? 0.75 : 1;
			},
			strokeDashoffset() {
				const offset = -1 * this.perimeter * (1 - this.rate) / 2;
				return `${offset}px`;
			},
			trailPathStyle() {
				return {
					strokeDasharray: `${(this.perimeter * this.rate)}px, ${this.perimeter}px`,
					strokeDashoffset: this.strokeDashoffset
				};
			},
			circlePathStyle() {
				return {
					strokeDasharray: `${this.perimeter * this.rate * (this.percentage / 100) }px, ${this.perimeter}px`,
					strokeDashoffset: this.strokeDashoffset,
					transition: 'stroke-dasharray 0.6s ease 0s, stroke 0.6s ease'
				};
			},
			stroke() {
				let ret;
				if (this.color) {
					ret = this.getCurrentColor(this.percentage);
				} else {
					switch (this.status) {
						case 'success':
							ret = '#13ce66';
							break;
						case 'exception':
							ret = '#ff4949';
							break;
						case 'warning':
							ret = '#e6a23c';
							break;
						default:
							ret = '#20a0ff';
					}
				}
				return ret;
			},
			iconClass() {
				if (this.status === 'warning') {
					return 'el-icon-warning';
				}
				if (this.type === 'line') {
					return this.status === 'success' ? 'el-icon-circle-check' : 'el-icon-circle-close';
				} else {
					return this.status === 'success' ? 'el-icon-check' : 'el-icon-close';
				}
			},
			progressTextSize() {
				return this.type === 'line'
					? 12 + this.strokeWidth * 0.4
					: this.width * 0.111111 + 2 ;
			},
			content() {
				if (typeof this.format === 'function') {
					return this.format(this.percentage) || '';
				} else {
					return `${this.percentage}%`;
				}
			}
		},
		methods: {
			getCurrentColor(percentage) {
				if (typeof this.color === 'function') {
					return this.color(percentage);
				} else if (typeof this.color === 'string') {
					return this.color;
				} else {
					return this.getLevelColor(percentage);
				}
			},
			getLevelColor(percentage) {
				const colorArray = this.getColorArray().sort((a, b) => a.percentage - b.percentage);
				for (let i = 0; i < colorArray.length; i++) {
					if (colorArray[i].percentage > percentage) {
						return colorArray[i].color;
					}
				}
				return colorArray[colorArray.length - 1].color;
			},
			getColorArray() {
				const color = this.color;
				const span = 100 / color.length;
				return color.map((seriesColor, index) => {
					if (typeof seriesColor === 'string') {
						return {
							color: seriesColor,
							percentage: (index + 1) * span
						};
					}
					return seriesColor;
				});
			}
		}
	};
</script>